home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue24 / appbar11 / APPBAR11.ZIP / APPBAR.PAS next >
Encoding:
Pascal/Delphi Source File  |  1997-07-02  |  53.3 KB  |  1,591 lines

  1. {*****************************************************************************}
  2. {                                                                             }
  3. { TAppBar Class v1.1                                                          }
  4. { implements Application Desktop Toolbar                                      }
  5. { (based on J.Richter's CAppBar MFC Class)                                    }
  6. {                                                                             }
  7. { Copyright (c) 1997 Paolo Giacomuzzi                                         }
  8. { e-mail: paolo.giacomuzzi@usa.net                                            }
  9. { http://www.geocities.com/SiliconValley/9486                                 }
  10. {                                                                             }
  11. {*****************************************************************************}
  12.  
  13. unit AppBar;
  14.  
  15.  
  16. interface
  17.  
  18.  
  19. uses
  20.   Windows, Messages, SysUtils, Classes, Forms, Dialogs, Controls, ExtCtrls,
  21.   ShellApi;
  22.  
  23.  
  24. const
  25.   // AppBar's user notification message
  26.   WM_APPBARNOTIFY = WM_USER + 100;
  27.  
  28.   // Timer interval
  29.   AUTO_HIDE_TIMER_INTERVAL = 400; // milliseconds
  30.  
  31.   // Defaults
  32.   AB_DEF_SIZE_INC = 1;
  33.   AB_DEF_DOCK_DIM = 32;
  34.  
  35.  
  36. type
  37.   // You can send to the Windows shell one of the following messages:
  38.   // Message             Description
  39.   // --------------      --------------------------------------------------
  40.   // ABM_NEW             Register a new AppBar to the system
  41.   // ABM_REMOVE          Remove a previously created AppBar from the system
  42.   // ABM_QUERYPOS        Query the AppBar position
  43.   // ABM_SETPOS          Set the AppBar position
  44.   // ABM_GETSTATE        Get the edge the Appbar is docked to
  45.   // ABM_GETTASKBARPOS   Get the Explorer Taskbar position
  46.   // ABM_ACTIVATE        Activate the AppBar
  47.   // ABM_GETAUTOHIDEBAR  Query if AppBar has Auto-hide behavior
  48.   // ABM_SETAUTOHIDEBAR  Set the AppBar's Auto-hide behavior
  49.  
  50.   // The ABM_message constants are defined in SHELLAPI.PAS as follows:
  51.   // ABM_NEW              = $00000000;
  52.   // ABM_REMOVE           = $00000001;
  53.   // ABM_QUERYPOS         = $00000002;
  54.   // ABM_SETPOS           = $00000003;
  55.   // ABM_GETSTATE         = $00000004;
  56.   // ABM_GETTASKBARPOS    = $00000005;
  57.   // ABM_ACTIVATE         = $00000006;
  58.   // ABM_GETAUTOHIDEBAR   = $00000007;
  59.   // ABM_SETAUTOHIDEBAR   = $00000008;
  60.   // ABM_WINDOWPOSCHANGED = $00000009;
  61.  
  62.   // The following enumerated type defines the constants in the table
  63.   TAppBarMessage = (abmNew, abmRemove, abmQueryPos, abmSetPos, abmGetState,
  64.                     abmGetTaskBarPos, abmActivate, abmGetAutoHideBar,
  65.                     abmSetAutoHideBar, abmWindowPosChanged);
  66.  
  67.   // An AppBar can be in one of 6 states shown in the table below:
  68.   // State          Description
  69.   // -----------    -----------------------------------------------------
  70.   // ABE_UNKNOWN    The Appbar is in an unknown state
  71.   //                (usually during construction/destruction)
  72.   // ABE_FLOAT      The AppBar is floating on the screen
  73.   // ABE_LEFT       The Appbar is docked on the left   edge of the screen
  74.   // ABE_TOP        The Appbar is docked on the top    edge of the screen
  75.   // ABE_RIGHT      The Appbar is docked on the right  edge of the screen
  76.   // ABE_BOTTOM     The Appbar is docked on the bottom edge of the screen
  77.  
  78.   // The ABE_edge state constants are defined in SHELLAPI.PAS as follows:
  79.   // ABE_LEFT    = 0;
  80.   // ABE_TOP     = 1;
  81.   // ABE_RIGHT   = 2;
  82.   // ABE_BOTTOM  = 3;
  83.  
  84.   // The ABE_UNKNOWN and ABE_FLOAT constants are defined here as follows:
  85.   // ABE_UNKNOWN = 4;
  86.   // ABE_FLOAT   = 5;
  87.  
  88.   // The following enumerated type defines the constants in the table
  89.   // (Values are mutually exclusive)
  90.   TAppBarEdge = (abeLeft, abeTop, abeRight, abeBottom, abeUnknown, abeFloat);
  91.  
  92.   // An AppBar can have several behavior flags as shown below:
  93.   // Flag                        Description
  94.   // --------------------------- -----------------------------------
  95.   // ABF_ALLOWLEFT               Allow dock on left   of screen
  96.   // ABF_ALLOWRIGHT              Allow dock on right  of screen
  97.   // ABF_ALLOWTOP                Allow dock on top    of screen
  98.   // ABF_ALLOWBOTTOM             Allow dock on bottom of screen
  99.   // ABF_ALLOWFLOAT              Allow float in the middle of screen
  100.  
  101.   // The following enumerated type defines the constants in the table
  102.   TAppBarFlag = (abfAllowLeft, abfAllowTop, abfAllowRight, abfAllowBottom,
  103.                  abfAllowFloat);
  104.   TAppBarFlags = set of TAppBarFlag;
  105.  
  106.   // The record below contains all of the AppBar settings that
  107.   // can be saved/loaded in/from the Registry
  108.   TAppBarSettings = record
  109.     cbSize       : DWORD;       // Size of this structure
  110.     abEdge       : TAppBarEdge; // ABE_UNKNOWN, ABE_FLOAT, or ABE_edge
  111.     bAutohide    : Boolean;     // Should AppBar be auto-hidden when docked?
  112.     bAlwaysOnTop : Boolean;     // Should AppBar always be on top?
  113.     rcDockDims   : TRect;       // Width/height for docked bar on 4 edges
  114.     rcFloat      : TRect;       // Floating rectangle (in screen coordinates)
  115.     nMinWidth    : LongInt;     // Min allowed width
  116.     nMinHeight   : LongInt;     // Min allowed height
  117.     nMaxWidth    : LongInt;     // Max allowed width
  118.     nMaxHeight   : LongInt;     // Max allowed height
  119.   end;
  120.  
  121.   // TAppBar class ////////////////////////////////////////////////////////////
  122.   TAppBar = class(TForm)
  123.  
  124.   private
  125.  
  126.   { Internal implementation state variables }
  127.  
  128.     // This AppBar's settings info
  129.     FABS : TAppBarSettings;
  130.  
  131.     // ABF_* flags
  132.     FabFlags : TAppBarFlags;
  133.  
  134.     // Discrete width/height size increments
  135.     FszSizeInc : TSize;
  136.  
  137.     // We need a member variable which tracks the proposed edge of the
  138.     // AppBar while the user is moving it, deciding where to position it.
  139.     // While not moving, this member must contain ABE_UNKNOWN so that
  140.     // GetEdge returns the current edge contained in FABS.abEdge.
  141.     // While moving the AppBar, FabEdgeProposedPrev contains the
  142.     // proposed edge based on the position of the AppBar.  The proposed
  143.     // edge becomes the new edge when the user stops moving the AppBar.
  144.     FabEdgeProposedPrev : TAppBarEdge;
  145.  
  146.     // We need a member variable which tracks whether a full screen
  147.     // application window is open
  148.     FbFullScreenAppOpen : Boolean;
  149.  
  150.     // We need a member variable which tracks whether our autohide window
  151.     // is visible or not
  152.     FbAutoHideIsVisible : Boolean;
  153.  
  154.     // We need a timer to to determine when the AppBar should be re-hidden
  155.     FTimer : TTimer;
  156.  
  157.   { Internal implementation functions }
  158.  
  159.     // Modifies window creation flags
  160.     procedure CreateParams (var Params: TCreateParams); override;
  161.  
  162.     // These functions encapsulate the shell's SHAppBarMessage function
  163.     function AppBarMessage (abMessage : TAppBarMessage;
  164.                             abEdge    : TAppBarEdge;
  165.                             lParam    : LPARAM;
  166.                             bRect     : Boolean;
  167.                             var rc    : TRect) : UINT;
  168.  
  169.     function AppBarMessage1 (abMessage : TAppBarMessage) : UINT;
  170.  
  171.     function AppBarMessage2 (abMessage : TAppBarMessage;
  172.                              abEdge    : TAppBarEdge) : UINT;
  173.  
  174.     function AppBarMessage3 (abMessage : TAppBarMessage;
  175.                              abEdge    : TAppBarEdge;
  176.                              lParam    : LPARAM) : UINT;
  177.  
  178.     function AppBarMessage4 (abMessage : TAppBarMessage;
  179.                              abEdge    : TAppBarEdge;
  180.                              lParam    : LPARAM;
  181.                              var rc    : TRect) : UINT;
  182.  
  183.     // Gets a edge (ABE_FLOAT or ABE_edge) from a point (screen coordinates)
  184.     function CalcProposedState (var pt : TSmallPoint) : TAppBarEdge;
  185.  
  186.     // Gets a retangle position (screen coordinates) from a proposed state
  187.     procedure GetRect (abEdgeProposed : TAppBarEdge; var rcProposed : TRect);
  188.  
  189.     // Adjusts the AppBar's location to account for autohide
  190.     // Returns TRUE if rectangle was adjusted
  191.     function AdjustLocationForAutohide (bShow  : Boolean;
  192.                                         var rc : TRect) : Boolean;
  193.  
  194.     // If AppBar is Autohide and docked, shows/hides the AppBar
  195.     procedure ShowHiddenAppBar (bShow : Boolean);
  196.  
  197.     // When Autohide AppBar is shown/hidden, slides in/out of view
  198.     procedure SlideWindow (var rcEnd : TRect);
  199.  
  200.     // Returns which edge we're autohidden on or ABE_UNKNOWN
  201.     function GetAutohideEdge : TAppBarEdge;
  202.  
  203.     // Returns a TSmallPoint that gives the cursor position in screen coords
  204.     function GetMessagePosition : TSmallPoint;
  205.  
  206.     // Changes the style of a window (translated from AfxModifyStyle)
  207.     function ModifyStyle (hWnd         : THandle;
  208.                           nStyleOffset : Integer;
  209.                           dwRemove     : DWORD;
  210.                           dwAdd        : DWORD;
  211.                           nFlags       : UINT) : Boolean;
  212.  
  213.   protected
  214.  
  215.   { Property selector functions }
  216.  
  217.     // Retrieves the AppBar's edge.  If the AppBar is being positioned, its
  218.     // proposed state is returned instead
  219.     function GetEdge : TAppBarEdge;
  220.  
  221.     // Changes the AppBar's edge to ABE_UNKNOWN, ABE_FLOAT or an ABE_edge
  222.     procedure SetEdge (abEdge : TAppBarEdge);
  223.  
  224.     // Returns TRUE if Auto-hide is on, FALSE if Auto-hide is off
  225.     function IsAutoHide : Boolean;
  226.  
  227.     // Sets the Auto-hide behavior
  228.     procedure SetAutoHide (bAutoHide : Boolean);
  229.  
  230.     // Returns TRUE if AppBar is always on topAuto-hide, FALSE otherwise
  231.     function IsAlwaysOnTop : Boolean;
  232.  
  233.     // Sets the AlwaysOnTop behavior
  234.     procedure SetAlwaysOnTop (bAlwaysOnTop : Boolean);
  235.  
  236.     // Gets the AppBar's floating rectangle
  237.     function GetFloatRect : TRect;
  238.  
  239.     // Sets the AppBar's floating rectangle
  240.     procedure SetFloatRect (rc : TRect);
  241.  
  242.     // Gets the AppBar's docked dimensions
  243.     function GetDockDims : TRect;
  244.  
  245.     // Sets the AppBar's docked dimensions
  246.     procedure SetDockDims (rc : TRect);
  247.  
  248.   { Overridable functions }
  249.  
  250.     // Called when the AppBar's proposed state changes
  251.     procedure OnAppBarStateChange (bProposed      : Boolean;
  252.                                    abEdgeProposed : TAppBarEdge); virtual;
  253.  
  254.     // Called if user attempts to dock an Autohide AppBar on
  255.     // an edge that already contains an Autohide AppBar
  256.     procedure OnAppBarForcedToDocked; virtual;
  257.  
  258.     // Called when AppBar gets an ABN_FULLSCREENAPP notification
  259.     procedure OnABNFullScreenApp (bOpen : Boolean); virtual;
  260.  
  261.     // Called when AppBar gets an ABN_POSCHANGED notification
  262.     procedure OnABNPosChanged; virtual;
  263.  
  264.     // Called when AppBar gets an ABN_WINDOWARRANGE notification
  265.     procedure OnABNWindowArrange (bBeginning : Boolean); virtual;
  266.  
  267.   { Message handlers }
  268.  
  269.     // Called when the AppBar receives a WM_APPBARNOTIFY window message
  270.     procedure OnAppBarCallbackMsg(var Msg : TMessage); message WM_APPBARNOTIFY;
  271.  
  272.     // Called when the AppBar form is first created
  273.     procedure OnCreate (var Msg: TWMCreate); message WM_CREATE;
  274.  
  275.     // Called when the AppBar form is about to be destroyed
  276.     procedure OnDestroy (var Msg : TWMDestroy); message WM_DESTROY;
  277.  
  278.     // Called when the AppBar receives a WM_WINDOWPOSCHANGED message
  279.     procedure OnWindowPosChanged (var Msg : TWMWindowPosChanged);
  280.                                                    message WM_WINDOWPOSCHANGED;
  281.  
  282.     // Called when the AppBar receives a WM_ACTIVATE message
  283.     procedure OnActivate (var Msg : TWMActivate); message WM_ACTIVATE;
  284.  
  285.     // Called every timer tick
  286.     procedure OnAppBarTimer (Sender : TObject);
  287.  
  288.     // Called when the AppBar receives a WM_NCMOUSEMOVE message
  289.     procedure OnNcMouseMove (var Msg : TWMNCMouseMove); message WM_NCMOUSEMOVE;
  290.  
  291.     // Called when the AppBar receives a WM_NCHITTEST message
  292.     procedure OnNcHitTest (var Msg: TWMNCHitTest); message WM_NCHITTEST;
  293.  
  294.     // Called when the AppBar receives a WM_ENTERSIZEMOVE message
  295.     procedure OnEnterSizeMove (var Msg : TMessage); message WM_ENTERSIZEMOVE;
  296.  
  297.     // Called when the AppBar receives a WM_EXITSIZEMOVE message
  298.     procedure OnExitSizeMove (var Msg : TMessage); message WM_EXITSIZEMOVE;
  299.  
  300.     // Called when the AppBar receives a WM_MOVING message
  301.     procedure OnMoving (var Msg : TMessage); message WM_MOVING;
  302.  
  303.     // Called when the AppBar receives a WM_SIZING message
  304.     procedure OnSizing (var Msg : TMessage); message WM_SIZING;
  305.  
  306.     // Called when the AppBar receives a WM_GETMINMAXINFO message
  307.     procedure OnGetMinMaxInfo (var Msg : TWMGetMinMaxInfo);
  308.                                                       message WM_GETMINMAXINFO;
  309.  
  310.   { AppBar-specific helper functions }
  311.  
  312.     // Returns TRUE if abEdge is ABE_LEFT or ABE_RIGHT, else FALSE is returned
  313.     function IsEdgeLeftOrRight (abEdge : TAppBarEdge) : Boolean;
  314.  
  315.     // Returns TRUE if abEdge is ABE_TOP or ABE_BOTTOM, else FALSE is returned
  316.     function IsEdgeTopOrBottom (abEdge : TAppBarEdge) : Boolean;
  317.  
  318.     // Returns TRUE if abEdge is ABE_FLOAT, else FALSE is returned
  319.     function IsFloating (abEdge : TAppBarEdge) : Boolean;
  320.  
  321.     // Returns TRUE if abFlags contain an at least allowed edge to dock on
  322.     function IsDockable (abFlags : TAppBarFlags) : Boolean;
  323.  
  324.     // Returns TRUE if abFlags contain abfAllowLeft and abfAllowRight
  325.     function IsDockableVertically (abFlags : TAppBarFlags) : Boolean;
  326.  
  327.     // Returns TRUE if abFlags contain abfAllowTop and abfAllowBottom
  328.     function IsDockableHorizontally (abFlags : TAppBarFlags) : Boolean;
  329.  
  330.     // Forces the shell to update its AppBar list and the workspace area
  331.     procedure ResetSystemKnowledge;
  332.  
  333.     // Returns a proposed edge or ABE_FLOAT based on ABF_* flags and a
  334.     // point specified in screen coordinates
  335.     function GetEdgeFromPoint (abFlags : TAppBarFlags;
  336.                                pt      : TSmallPoint) : TAppBarEdge;
  337.  
  338.   public
  339.  
  340.   { Public member functions }
  341.  
  342.     // Constructs an AppBar
  343.     constructor Create (Owner : TComponent); override;
  344.  
  345.     // Destroys a previously created AppBar
  346.     destructor Destroy; override;
  347.  
  348.     // Forces the AppBar's visual appearance to match its internal state
  349.     procedure UpdateBar;
  350.  
  351.   published
  352.  
  353.   { Properties }
  354.  
  355.     // Allowed dockable edges
  356.     property Flags : TAppBarFlags read FabFlags write FabFlags;
  357.  
  358.     // Horizontal size increment
  359.     property HorzSizeInc : LongInt read FszSizeInc.cx write FszSizeInc.cx;
  360.  
  361.     // Vertical size increment
  362.     property VertSizeInc : LongInt read FszSizeInc.cy write FszSizeInc.cy;
  363.  
  364.     // Edge to dock on
  365.     property Edge : TAppBarEdge read GetEdge write SetEdge;
  366.  
  367.     // Auto-hide On/Off
  368.     property AutoHide : Boolean read IsAutoHide write SetAutoHide;
  369.  
  370.     // Always On Top On/Off
  371.     property AlwaysOnTop : Boolean read IsAlwaysOnTop write SetAlwaysOnTop;
  372.  
  373.     // Dimensions when docked on left, top, right and bottom
  374.     property DockDims : TRect read GetDockDims write SetDockDims;
  375.  
  376.     // AppBar rectangle when floating
  377.     property FloatRect : TRect read GetFloatRect write SetFloatRect;
  378.  
  379.     // AppBar MinMax dimensions when floating
  380.     property MinWidth  : LongInt read FABS.nMinWidth  write FABS.nMinWidth;
  381.     property MinHeight : LongInt read FABS.nMinHeight write FABS.nMinHeight;
  382.     property MaxWidth  : LongInt read FABS.nMaxWidth  write FABS.nMaxWidth;
  383.     property MaxHeight : LongInt read FABS.nMaxHeight write FABS.nMaxHeight;
  384.  
  385.   end;
  386.  
  387. implementation
  388.  
  389.  
  390. { Internal implementation functions }
  391.  
  392.  
  393. // TAppBar.CreateParams ///////////////////////////////////////////////////////
  394. procedure TAppBar.CreateParams (var Params: TCreateParams);
  395. var
  396.   dwAdd, dwRemove, dwAddEx, dwRemoveEx : DWORD;
  397. begin
  398.   // Call the inherited first
  399.   inherited CreateParams(Params);
  400.  
  401.   // Styles to be added
  402.   dwAdd := 0;
  403.   dwAddEx := WS_EX_TOOLWINDOW;
  404.  
  405.   // Styles to be removed
  406.   dwRemove := WS_SYSMENU or WS_MAXIMIZEBOX or WS_MINIMIZEBOX;
  407.   dwRemoveEx := WS_EX_APPWINDOW;
  408.  
  409.   // Modify style flags
  410.   with Params do begin
  411.     Style := Style and (not dwRemove);
  412.     Style := Style or dwAdd;
  413.     ExStyle := ExStyle and (not dwRemoveEx);
  414.     ExStyle := ExStyle or dwAddEx;
  415.   end;
  416. end;
  417.  
  418.  
  419. // TAppBar.AppBarMessage //////////////////////////////////////////////////////
  420. function TAppBar.AppBarMessage (abMessage : TAppBarMessage;
  421.                                 abEdge    : TAppBarEdge;
  422.                                 lParam    : LPARAM;
  423.                                 bRect     : Boolean;
  424.                                 var rc    : TRect) : UINT;
  425. var
  426.   abd : TAppBarData;
  427. begin
  428.   // Initialize an APPBARDATA structure
  429.   abd.cbSize := sizeof(abd);
  430.   abd.hWnd := Handle;
  431.   abd.uCallbackMessage := WM_APPBARNOTIFY;
  432.   abd.uEdge := Ord(abEdge);
  433.   if bRect then
  434.     abd.rc := rc
  435.   else
  436.     abd.rc := Rect(0, 0, 0, 0);
  437.   abd.lParam := lParam;
  438.   Result := SHAppBarMessage(Ord(abMessage), abd);
  439.  
  440.   // If the caller passed a rectangle, return the updated rectangle
  441.   if bRect then
  442.     rc := abd.rc;
  443. end;
  444.  
  445.  
  446. // TAppBar.AppBarMessage1 /////////////////////////////////////////////////////
  447. function TAppBar.AppBarMessage1 (abMessage : TAppBarMessage) : UINT;
  448. var
  449.   rc : TRect;
  450. begin
  451.   Result := AppBarMessage(abMessage, abeFloat, 0, False, rc);
  452. end;
  453.  
  454.  
  455. // TAppBar.AppBarMessage2 /////////////////////////////////////////////////////
  456. function TAppBar.AppBarMessage2 (abMessage : TAppBarMessage;
  457.                                  abEdge    : TAppBarEdge) : UINT;
  458. var
  459.   rc : TRect;
  460. begin
  461.   Result := AppBarMessage(abMessage, abEdge, 0, False, rc);
  462. end;
  463.  
  464.  
  465. // TAppBar.AppBarMessage3 /////////////////////////////////////////////////////
  466. function TAppBar.AppBarMessage3 (abMessage : TAppBarMessage;
  467.                                  abEdge    : TAppBarEdge;
  468.                                  lParam    : LPARAM) : UINT;
  469. var
  470.   rc : TRect;
  471. begin
  472.   Result := AppBarMessage(abMessage, abEdge, lParam, False, rc);
  473. end;
  474.  
  475.  
  476. // TAppBar.AppBarMessage4 /////////////////////////////////////////////////////
  477. function TAppBar.AppBarMessage4 (abMessage : TAppBarMessage;
  478.                                  abEdge    : TAppBarEdge;
  479.                                  lParam    : LPARAM;
  480.                                  var rc    : TRect) : UINT;
  481. begin
  482.   Result := AppBarMessage(abMessage, abEdge, lParam, True, rc);
  483. end;
  484.  
  485.  
  486. // TAppBar.CalcProposedState //////////////////////////////////////////////////
  487. function TAppBar.CalcProposedState (var pt : TSmallPoint) : TAppBarEdge;
  488. var
  489.   bForceFloat : Boolean;
  490. begin
  491.   // Force the AppBar to float if the user is holding down the Ctrl key
  492.   // and the AppBar's style allows floating
  493.   bForceFloat := ((GetKeyState(VK_CONTROL) and $8000) <> 0) and
  494.                  (abfAllowFloat in FabFlags);
  495.   if bForceFloat then
  496.     Result := abeFloat
  497.   else
  498.     Result := GetEdgeFromPoint(FabFlags, pt);
  499. end;
  500.  
  501.  
  502. // TAppBar.GetRect ////////////////////////////////////////////////////////////
  503. procedure TAppBar.GetRect (abEdgeProposed : TAppBarEdge;
  504.                            var rcProposed : TRect);
  505. begin
  506.   // This function finds the x, y, cx, cy of the AppBar window
  507.   if abEdgeProposed = abeFloat then begin
  508.     // The AppBar is floating, the proposed rectangle is correct
  509.   end else begin
  510.     // The AppBar is docked or auto-hide
  511.     // Set dimensions to full screen
  512.     with rcProposed do begin
  513.       Left   := 0;
  514.       Top    := 0;
  515.       Right  := GetSystemMetrics(SM_CXSCREEN);
  516.       Bottom := GetSystemMetrics(SM_CYSCREEN);
  517.     end;
  518.  
  519.     // Subtract off what we want from the full screen dimensions
  520.     if not IsAutohide then
  521.       // Ask the shell where we can dock
  522.       AppBarMessage4(abmQueryPos, abEdgeProposed, LPARAM(False), rcProposed);
  523.  
  524.     case abEdgeProposed of
  525.       abeLeft:
  526.         rcProposed.Right  := rcProposed.Left   + FABS.rcDockDims.Left;
  527.       abeTop:
  528.         rcProposed.Bottom := rcProposed.Top    + FABS.rcDockDims.Top;
  529.       abeRight:
  530.         rcProposed.Left   := rcProposed.Right  - FABS.rcDockDims.Right;
  531.       abeBottom:
  532.         rcProposed.Top    := rcProposed.Bottom - FABS.rcDockDims.Bottom;
  533.     end; // end of case
  534.  
  535.   end; // end of else
  536. end;
  537.  
  538.  
  539. // TAppBar.AdjustLocationForAutohide //////////////////////////////////////////
  540. function TAppBar.AdjustLocationForAutohide (bShow  : Boolean;
  541.                                             var rc : TRect) : Boolean;
  542. var
  543.   x, y : Integer;
  544.   cxVisibleBorder, cyVisibleBorder : Integer;
  545. begin
  546.   if ((GetEdge = abeUnknown) or (GetEdge = abeFloat) or
  547.       (not IsAutohide)) then begin
  548.     // If we are not docked on an edge OR we are not auto-hidden, there is
  549.     // nothing for us to do; just return
  550.     Result := False;
  551.     Exit;
  552.   end;
  553.  
  554.   // Showing/hiding doesn't change our size; only our position
  555.   x := 0; y := 0; // Assume a position of (0, 0)
  556.  
  557.   if bShow then
  558.     // If we are on the right or bottom, calculate our visible position
  559.     case GetEdge of
  560.       abeRight:
  561.         x := GetSystemMetrics(SM_CXSCREEN) - (rc.Right - rc.Left);
  562.       abeBottom:
  563.         y := GetSystemMetrics(SM_CYSCREEN) - (rc.Bottom - rc.Top);
  564.     end
  565.   else begin
  566.     // Keep a part of the AppBar visible at all times
  567.     cxVisibleBorder := 2 * GetSystemMetrics(SM_CXBORDER);
  568.     cyVisibleBorder := 2 * GetSystemMetrics(SM_CYBORDER);
  569.  
  570.     // Calculate our x or y coordinate so that only the border is visible
  571.     case GetEdge of
  572.       abeLeft:
  573.         x := -((rc.Right - rc.Left) - cxVisibleBorder);
  574.       abeRight:
  575.         x := GetSystemMetrics(SM_CXSCREEN) - cxVisibleBorder;
  576.       abeTop:
  577.         y := -((rc.Bottom - rc.Top) - cyVisibleBorder);
  578.       abeBottom:
  579.         y := GetSystemMetrics(SM_CYSCREEN) - cyVisibleBorder;
  580.     end;
  581.   end;
  582.  
  583.   with rc do begin
  584.     Right  := x + (Right - Left);
  585.     Bottom := y + (Bottom - Top);
  586.     Left   := x;
  587.     Top    := y;
  588.   end;
  589.  
  590.   Result := True;
  591. end;
  592.  
  593.  
  594. // TAppBar.ShowHiddenAppBar ///////////////////////////////////////////////////
  595. procedure TAppBar.ShowHiddenAppBar (bShow : Boolean);
  596. var
  597.   rc : TRect;
  598. begin
  599.   // Get our window location in screen coordinates
  600.   GetWindowRect(Handle, rc);
  601.  
  602.   // Assume  that we are visible
  603.   FbAutoHideIsVisible := True;
  604.  
  605.   if AdjustLocationForAutohide(bShow, rc) then begin
  606.     // the rectangle was adjusted, we are an autohide bar
  607.     // Rememebr whether we are visible or not
  608.     FbAutoHideIsVisible := bShow;
  609.  
  610.     // Slide window in from or out to the edge
  611.     SlideWindow(rc);
  612.   end;
  613. end;
  614.  
  615.  
  616. // TAppBar.SlideWindow ////////////////////////////////////////////////////////
  617. procedure TAppBar.SlideWindow (var rcEnd : TRect);
  618. var
  619.   bFullDragOn : Boolean;
  620.   rcStart : TRect;
  621.   dwTimeStart, dwTimeEnd, dwTime : DWORD;
  622.   x, y, w, h : Integer;
  623. begin
  624.   // Only slide the window if the user has FullDrag turned on
  625.   SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, @bFullDragOn, 0);
  626.  
  627.   // Get the current window position
  628.   GetWindowRect(Handle, rcStart);
  629.   if (bFullDragOn and
  630.       ((rcStart.Left   <> rcEnd.Left  ) or
  631.        (rcStart.Top    <> rcEnd.Top   ) or
  632.        (rcStart.Right  <> rcEnd.Right ) or
  633.        (rcStart.Bottom <> rcEnd.Bottom))) then begin
  634.  
  635.     // Get our starting and ending time
  636.     dwTimeStart := GetTickCount;
  637.     dwTimeEnd := dwTimeStart + AUTO_HIDE_TIMER_INTERVAL;
  638.     dwTime := dwTimeStart;
  639.     while (dwTime < dwTimeEnd) do begin
  640.       // While we are still sliding, calculate our new position
  641.       x := rcStart.Left - (rcStart.Left - rcEnd.Left)
  642.            * Integer(dwTime - dwTimeStart) div AUTO_HIDE_TIMER_INTERVAL;
  643.  
  644.       y := rcStart.Top  - (rcStart.Top  - rcEnd.Top)
  645.            * Integer(dwTime - dwTimeStart) div AUTO_HIDE_TIMER_INTERVAL;
  646.  
  647.       w := (rcStart.Right - rcStart.Left)
  648.            - ((rcStart.Right - rcStart.Left) - (rcEnd.Right - rcEnd.Left))
  649.            * Integer(dwTime - dwTimeStart) div AUTO_HIDE_TIMER_INTERVAL;
  650.  
  651.       h := (rcStart.Bottom - rcStart.Top)
  652.            - ((rcStart.Bottom - rcStart.Top) - (rcEnd.Bottom - rcEnd.Top))
  653.            * Integer(dwTime - dwTimeStart) div AUTO_HIDE_TIMER_INTERVAL;
  654.  
  655.       // Show the window at its changed position
  656.       SetWindowPos(Handle, 0, x, y, w, h,
  657.                    SWP_NOZORDER or SWP_NOACTIVATE or SWP_DRAWFRAME);
  658.       UpdateWindow(Handle);
  659.       dwTime := GetTickCount;
  660.     end;
  661.   end;
  662.  
  663.   // Make sure that the window is at its final position
  664.   Left   := rcEnd.Left;
  665.   Top    := rcEnd.Top;
  666.   Width  := rcEnd.Right - rcEnd.Left;
  667.   Height := rcEnd.Bottom - rcEnd.Top;
  668. end;
  669.  
  670.  
  671. // TAppBar.GetAutohideEdge ////////////////////////////////////////////////////
  672. function TAppBar.GetAutohideEdge : TAppBarEdge;
  673. begin
  674.   if Handle = AppBarMessage2(abmGetAutoHideBar, abeLeft) then
  675.     Result := abeLeft
  676.   else if Handle = AppBarMessage2(abmGetAutoHideBar, abeTop) then
  677.     Result := abeTop
  678.   else if Handle = AppBarMessage2(abmGetAutoHideBar, abeRight) then
  679.     Result := abeRight
  680.   else if Handle = AppBarMessage2(abmGetAutoHideBar, abeBottom) then
  681.     Result := abeBottom
  682.   else
  683.     // NOTE: If AppBar is docked but not auto-hidden, we return ABE_UNKNOWN
  684.     Result := abeUnknown;
  685. end;
  686.  
  687.  
  688. // TAppBar.GetMessagePosition /////////////////////////////////////////////////
  689. function TAppBar.GetMessagePosition : TSmallPoint;
  690. var
  691.   pt : TSmallPoint;
  692.   dw : DWORD;
  693. begin
  694.   dw := GetMessagePos;
  695.   pt.X := SHORT(dw);
  696.   pt.Y := SHORT((dw and $FFFF0000) shr 16);
  697.   Result := pt;
  698. end;
  699.  
  700.  
  701. // TAppBar.ModifyStyle ////////////////////////////////////////////////////////
  702. function TAppBar.ModifyStyle (hWnd : THandle;
  703.                               nStyleOffset : Integer;
  704.                               dwRemove     : DWORD;
  705.                               dwAdd        : DWORD;
  706.                               nFlags       : UINT) : Boolean;
  707. var
  708.   dwStyle : DWORD;
  709.   dwNewStyle : DWORD;
  710. begin
  711.   dwStyle := GetWindowLong(hWnd, nStyleOffset);
  712.   dwNewStyle := (dwStyle and (not dwRemove)) or dwAdd;
  713.  
  714.   if dwStyle = dwNewStyle then begin
  715.     Result := False;
  716.     Exit;
  717.   end;
  718.  
  719.   SetWindowLong(hWnd, nStyleOffset, dwNewStyle);
  720.  
  721.   if nFlags <> 0 then
  722.     SetWindowPos(hWnd, 0, 0, 0, 0, 0,
  723.       SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE or nFlags);
  724.  
  725.   Result := True;
  726. end;
  727.  
  728.  
  729. { Property selector functions }
  730.  
  731.  
  732. // TAppBar.GetEdge ////////////////////////////////////////////////////////////
  733. function TAppBar.GetEdge : TAppBarEdge;
  734. begin
  735.   if FabEdgeProposedPrev <> abeUnknown then
  736.     Result := FabEdgeProposedPrev
  737.   else
  738.     Result := FABS.abEdge;
  739. end;
  740.  
  741.  
  742. // TAppBar.SetEdge ////////////////////////////////////////////////////////////
  743. procedure TAppBar.SetEdge (abEdge : TAppBarEdge);
  744. var
  745.   abCurrentEdge : TAppBarEdge;
  746.   currentRect : TRect;
  747.   rc : TRect;
  748.   hWnd : THandle;
  749. begin
  750.   // If the AppBar is registered as auto-hide, unregister it
  751.   abCurrentEdge := GetAutohideEdge;
  752.  
  753.   if abCurrentEdge <> abeUnknown then
  754.     // Our AppBar is auto-hidden, unregister it
  755.     AppBarMessage3(abmSetAutoHideBar, abCurrentEdge, LPARAM(False));
  756.  
  757.   // Save the new requested state
  758.   FABS.abEdge := abEdge;
  759.  
  760.   case abEdge of
  761.  
  762.     abeUnknown: begin
  763.       // We are being completely unregistered.
  764.       // Probably, the AppBar window is being destroyed.
  765.       // If the AppBar is registered as NOT auto-hide, unregister it
  766.       AppBarMessage1(abmRemove);
  767.     end;
  768.  
  769.     abeFloat: begin
  770.       // We are floating and therefore are just a regular window.
  771.       // Tell the shell that the docked AppBar should be of 0x0 dimensions
  772.       // so that the workspace is not affected by the AppBar
  773.       currentRect := Rect(0, 0, 0, 0);
  774.       AppBarMessage4(abmSetPos, abEdge, LPARAM(False), currentRect);
  775.       Left   := FABS.rcFloat.Left;
  776.       Top    := FABS.rcFloat.Top;
  777.       Width  := FABS.rcFloat.Right - FABS.rcFloat.Left;
  778.       Height := FABS.rcFloat.Bottom - FABS.rcFloat.Top;
  779.     end;
  780.  
  781.     else begin
  782.       if IsAutohide and
  783.          (AppBarMessage3(abmSetAutoHideBar,
  784.                          GetEdge,
  785.                          LPARAM(True)) = 0) then begin
  786.         // We couldn't set the AppBar on a new edge, let's dock it instead
  787.         FABS.bAutohide := False;
  788.         // Call a virtual function to let derived classes know that the AppBar
  789.         // changed from auto-hide to docked
  790.         OnAppBarForcedToDocked;
  791.       end;
  792.  
  793.       GetRect(GetEdge, rc);
  794.       if IsAutohide then begin
  795.         currentRect := Rect(0, 0, 0, 0);
  796.         AppBarMessage4(abmSetPos, abeLeft, LPARAM(False), currentRect);
  797.       end else begin
  798.         // Tell the shell where the AppBar is
  799.         AppBarMessage4(abmSetPos, abEdge, LPARAM(False), rc);
  800.       end;
  801.  
  802.       AdjustLocationForAutohide(FbAutoHideIsVisible, rc);
  803.  
  804.       // Slide window in from or out to the edge
  805.       SlideWindow(rc);
  806.  
  807.     end; // end of else
  808.  
  809.   end; // end of case
  810.  
  811.   // Set the AppBar's z-order appropriately
  812.   hWnd := HWND_NOTOPMOST; // Assume normal Z-Order
  813.   if FABS.bAlwaysOnTop then begin
  814.     // If we are supposed to be always-on-top, put us there
  815.     hWnd := HWND_TOPMOST;
  816.     if FbFullScreenAppOpen then
  817.       // But, if a full-screen window is opened, put ourself at the bottom
  818.       // of the z-order so that we don't cover the full-screen window
  819.       hWnd := HWND_BOTTOM;
  820.   end;
  821.   SetWindowPos(Handle,
  822.                hWnd,
  823.                0, 0, 0, 0,
  824.                SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE);
  825.  
  826.   // Make sure that any auto-hide appbars stay on top of us after we move
  827.   // even though our activation state has not changed
  828.   AppBarMessage1(abmActivate);
  829.  
  830.   // Tell our derived class that there is a state change
  831.   OnAppBarStateChange(False, abEdge);
  832. end;
  833.  
  834.  
  835. // TAppBar.IsAutoHide /////////////////////////////////////////////////////////
  836. function TAppBar.IsAutoHide : Boolean;
  837. begin
  838.   Result := FABS.bAutohide;
  839. end;
  840.  
  841.  
  842. // TAppBar.SetAutoHide ////////////////////////////////////////////////////////
  843. procedure TAppBar.SetAutoHide (bAutoHide : Boolean);
  844. begin
  845.   FABS.bAutohide := bAutoHide;
  846. end;
  847.  
  848.  
  849. // TAppBar.IsAlwaysOnTop //////////////////////////////////////////////////////
  850. function TAppBar.IsAlwaysOnTop : Boolean;
  851. begin
  852.   Result := FABS.bAlwaysOnTop;
  853. end;
  854.  
  855.  
  856. // TAppBar.SetAlwaysOnTop /////////////////////////////////////////////////////
  857. procedure TAppBar.SetAlwaysOnTop (bAlwaysOnTop : Boolean);
  858. begin
  859.   FABS.bAlwaysOnTop := bAlwaysOnTop;
  860. end;
  861.  
  862.  
  863. // TAppBar.GetFloatRect ///////////////////////////////////////////////////////
  864. function TAppBar.GetFloatRect : TRect;
  865. begin
  866.   Result := FABS.rcFloat;
  867. end;
  868.  
  869.  
  870. // TAppBar.SetFloatRect ///////////////////////////////////////////////////////
  871. procedure TAppBar.SetFloatRect (rc : TRect);
  872. begin
  873.   FABS.rcFloat := rc;
  874. end;
  875.  
  876.  
  877. // TAppBar.GetDockDims ////////////////////////////////////////////////////////
  878. function TAppBar.GetDockDims : TRect;
  879. begin
  880.   Result := FABS.rcDockDims;
  881. end;
  882.  
  883.  
  884. // TAppBar.SetDockDims ////////////////////////////////////////////////////////
  885. procedure TAppBar.SetDockDims (rc : TRect);
  886. begin
  887.   FABS.rcDockDims := rc;
  888. end;
  889.  
  890.  
  891. { Overridable functions }
  892.  
  893.  
  894. // TAppBar.OnAppBarStateChange ////////////////////////////////////////////////
  895. procedure TAppBar.OnAppBarStateChange (bProposed      : Boolean;
  896.                                        abEdgeProposed : TAppBarEdge);
  897. begin
  898.   // Hide the window adorments when docked
  899.   if abEdgeProposed = abeFloat then
  900.     ModifyStyle(Handle, GWL_STYLE, 0, WS_CAPTION or WS_SYSMENU, SWP_DRAWFRAME)
  901.   else
  902.     ModifyStyle(Handle, GWL_STYLE, WS_CAPTION or WS_SYSMENU, 0, SWP_DRAWFRAME);
  903. end;
  904.  
  905.  
  906. // TAppBar.OnAppBarForcedToDocked /////////////////////////////////////////////
  907. procedure TAppBar.OnAppBarForcedToDocked;
  908. const
  909.   CRLF = #10#13;
  910. begin
  911.   // Display the application name as the message box caption text.
  912.   MessageDlg('There is already an auto hidden window on this edge.' + CRLF +
  913.              'Only one auto hidden window is allowed on each edge.',
  914.              mtInformation,
  915.              [mbOk],
  916.              0);
  917. end;
  918.  
  919. // TAppBar.OnABNFullScreenApp /////////////////////////////////////////////////
  920. procedure TAppBar.OnABNFullScreenApp (bOpen : Boolean);
  921. begin
  922.   // This function is called when a FullScreen window is openning or
  923.   // closing. A FullScreen window is a top-level window that has its caption
  924.   // above the top of the screen allowing the entire screen to be occupied
  925.   // by the window's client area.
  926.  
  927.   // If the AppBar is a topmost window when a FullScreen window is activated,
  928.   // we need to change our window to a non-topmost window so that the AppBar
  929.   // doesn't cover the FullScreen window's client area.
  930.  
  931.   // If the FullScreen window is closing, we need to set the AppBar's
  932.   // Z-Order back to when the user wants it to be.
  933.   FbFullScreenAppOpen := bOpen;
  934.   UpdateBar;
  935. end;
  936.  
  937.  
  938. // TAppBar.OnABNPosChanged ////////////////////////////////////////////////////
  939. procedure TAppBar.OnABNPosChanged;
  940. begin
  941.   // The TaskBar or another AppBar has changed its size or position
  942.   if (GetEdge <> abeFloat) and (not IsAutohide) then
  943.     // If we're not floating and we're not auto-hidden, we have to
  944.     // reposition our window
  945.     UpdateBar;
  946. end;
  947.  
  948.  
  949. // TAppBar.OnABNWindowArrange /////////////////////////////////////////////////
  950. procedure TAppBar.OnABNWindowArrange (bBeginning : Boolean);
  951. begin
  952.   // This function intentionally left blank
  953. end;
  954.  
  955.  
  956. { Message handlers }
  957.  
  958.  
  959. // TAppBar.OnAppBarCallbackMsg ////////////////////////////////////////////////
  960. procedure TAppBar.OnAppBarCallbackMsg (var Msg : TMessage);
  961. begin
  962.   case Msg.WParam of
  963.  
  964.     ABN_FULLSCREENAPP:
  965.       OnABNFullScreenApp(Msg.LParam <> 0);
  966.  
  967.     ABN_POSCHANGED:
  968.       OnABNPosChanged;
  969.  
  970.     ABN_WINDOWARRANGE:
  971.       OnABNWindowArrange(Msg.LParam <> 0);
  972.   end;
  973. end;
  974.  
  975.  
  976. // TAppBar.OnCreate ///////////////////////////////////////////////////////////
  977. procedure TAppBar.OnCreate (var Msg : TWMCreate);
  978. begin
  979.   inherited;
  980.   // Associate a timer with the AppBar.  The timer is used to determine
  981.   // when a visible, inactive, auto-hide AppBar should be re-hidden
  982.   FTimer := TTimer.Create(Self);
  983.   with FTimer do begin
  984.     Interval := AUTO_HIDE_TIMER_INTERVAL;
  985.     OnTimer := OnAppBarTimer;
  986.     Enabled := True;
  987.   end;
  988.  
  989.   // Save the initial position of the floating AppBar
  990.   FABS.rcFloat.Left   := Left;
  991.   FABS.rcFloat.Top    := Top;
  992.  
  993.   // Register our AppBar window with the Shell
  994.   AppBarMessage1(abmNew);
  995.  
  996.   // Update AppBar internal state
  997.   UpdateBar;
  998.  
  999.   // Save the initial size of the floating AppBar
  1000.   PostMessage(Handle, WM_ENTERSIZEMOVE, 0, 0);
  1001.   PostMessage(Handle, WM_EXITSIZEMOVE,  0, 0);
  1002. end;
  1003.  
  1004.  
  1005. // TAppBar.OnDestroy //////////////////////////////////////////////////////////
  1006. procedure TAppBar.OnDestroy (var Msg : TWMDestroy);
  1007. begin
  1008.   // Free the Autohide timer
  1009.   FTimer.Enabled := False;
  1010.   FTimer.Free;
  1011.   // Unregister our AppBar window with the Shell
  1012.   SetEdge(abeUnknown);
  1013.   inherited;
  1014. end;
  1015.  
  1016.  
  1017. // TAppBar.OnWindowPosChanged /////////////////////////////////////////////////
  1018. procedure TAppBar.OnWindowPosChanged (var Msg : TWMWindowPosChanged);
  1019. begin
  1020.   inherited;
  1021.   // When our window changes position, tell the Shell so that any
  1022.   // auto-hidden AppBar on our edge stays on top of our window making it
  1023.   // always accessible to the user
  1024.   AppBarMessage1(abmWindowPosChanged);
  1025. end;
  1026.  
  1027.  
  1028. // TAppBar.OnActivate /////////////////////////////////////////////////////////
  1029. procedure TAppBar.OnActivate (var Msg : TWMActivate);
  1030. begin
  1031.   inherited;
  1032.   if Msg.Active = WA_INACTIVE then
  1033.     // Hide the AppBar if we are docked and auto-hidden
  1034.     ShowHiddenAppBar(False);
  1035.   // When our window changes position, tell the Shell so that any
  1036.   // auto-hidden AppBar on our edge stays on top of our window making it
  1037.   // always accessible to the user.
  1038.   AppBarMessage1(abmActivate);
  1039. end;
  1040.  
  1041.  
  1042. // TAppBar.OnAppBarTimer //////////////////////////////////////////////////////
  1043. procedure TAppBar.OnAppBarTimer (Sender : TObject);
  1044. var
  1045.   pt : TSmallPoint;
  1046.   rc : TRect;
  1047. begin
  1048.   if GetActiveWindow <> Handle then begin
  1049.     // Possibly hide the AppBar if we are not the active window
  1050.     // Get the position of the mouse and the AppBar's position
  1051.     // Everything must be in screen coordinates
  1052.     pt := GetMessagePosition;
  1053.     GetWindowRect(Handle, rc);
  1054.     // Add a little margin around the AppBar
  1055.     InflateRect(rc,
  1056.                 2 * GetSystemMetrics(SM_CXDOUBLECLK),
  1057.                 2 * GetSystemMetrics(SM_CYDOUBLECLK));
  1058.     if not PtInRect(rc, SmallPointToPoint(pt)) then
  1059.       // If the mouse is NOT over the AppBar, hide the AppBar
  1060.       ShowHiddenAppBar(False);
  1061.   end;
  1062.   inherited;
  1063. end;
  1064.  
  1065.  
  1066. // TAppBar.OnNcMouseMove //////////////////////////////////////////////////////
  1067. procedure TAppBar.OnNcMouseMove (var Msg : TWMNCMouseMove);
  1068. begin
  1069.   // If we are a docked, auto-hidden AppBar, shown us
  1070.   // when the user moves over our non-client area
  1071.   ShowHiddenAppBar(True);
  1072.   inherited;
  1073. end;
  1074.  
  1075.  
  1076. // TAppBar.OnNcHitTest ////////////////////////////////////////////////////////
  1077. procedure TAppBar.OnNcHitTest (var Msg: TWMNCHitTest);
  1078. var
  1079.   u : UINT;
  1080.   bPrimaryMouseBtnDown : Boolean;
  1081.   rcClient : TRect;
  1082.   pt : TPoint;
  1083.   vKey : Integer;
  1084. begin
  1085.   // Find out what the system thinks is the hit test code
  1086.   inherited;
  1087.   u := Msg.Result;
  1088.  
  1089.   // NOTE: If the user presses the secondary mouse button, pretend that the
  1090.   // user clicked on the client area so that we get WM_CONTEXTMENU messages
  1091.   if GetSystemMetrics(SM_SWAPBUTTON) <> 0 then
  1092.     vKey := VK_RBUTTON
  1093.   else
  1094.     vKey := VK_LBUTTON;
  1095.   bPrimaryMouseBtnDown := ((GetAsyncKeyState(vKey) and $8000) <> 0);
  1096.  
  1097.   if (u = HTCLIENT) and bPrimaryMouseBtnDown then
  1098.     // User clicked in client area, allow AppBar to move.  We get this
  1099.     // behavior by pretending that the user clicked on the caption area
  1100.     u := HTCAPTION;
  1101.  
  1102.   // If the AppBar is floating and the hittest code is a resize code...
  1103.   if ((GetEdge = abeFloat) and
  1104.       (HTSIZEFIRST <= u) and (u <= HTSIZELAST)) then begin
  1105.     case u of
  1106.       HTLEFT, HTRIGHT:
  1107.         if FszSizeInc.cx = 0
  1108.           then u := HTBORDER;
  1109.       HTTOP, HTBOTTOM:
  1110.         if FszSizeInc.cy = 0
  1111.           then u := HTBORDER;
  1112.       HTTOPLEFT:
  1113.         if (FszSizeInc.cx = 0) and (FszSizeInc.cy = 0)
  1114.           then u := HTBORDER
  1115.         else if (FszSizeInc.cx = 0) and (FszSizeInc.cy <> 0)
  1116.           then u := HTTOP
  1117.         else if (FszSizeInc.cx <> 0) and (FszSizeInc.cy = 0)
  1118.           then u := HTLEFT;
  1119.       HTTOPRIGHT:
  1120.         if (FszSizeInc.cx = 0) and (FszSizeInc.cy = 0)
  1121.           then u := HTBORDER
  1122.         else if (FszSizeInc.cx = 0) and (FszSizeInc.cy <> 0)
  1123.           then u := HTTOP
  1124.         else if (FszSizeInc.cx <> 0) and (FszSizeInc.cy = 0)
  1125.           then u := HTRIGHT;
  1126.       HTBOTTOMLEFT:
  1127.         if (FszSizeInc.cx = 0) and (FszSizeInc.cy = 0)
  1128.           then u := HTBORDER
  1129.         else if (FszSizeInc.cx = 0) and (FszSizeInc.cy <> 0)
  1130.           then u := HTBOTTOM
  1131.         else if (FszSizeInc.cx <> 0) and (FszSizeInc.cy = 0)
  1132.           then u := HTLEFT;
  1133.       HTBOTTOMRIGHT:
  1134.         if (FszSizeInc.cx = 0) and (FszSizeInc.cy = 0)
  1135.           then u := HTBORDER
  1136.         else if (FszSizeInc.cx = 0) and (FszSizeInc.cy <> 0)
  1137.           then u := HTBOTTOM
  1138.         else if (FszSizeInc.cx <> 0) and (FszSizeInc.cy = 0)
  1139.           then u := HTRIGHT;
  1140.     end;
  1141.   end;
  1142.  
  1143.   // When the AppBar is docked, the user can resize only one edge.
  1144.   // This next section determines which edge the user can resize.
  1145.   // To allow resizing, the AppBar window must have the WS_THICKFRAME style
  1146.  
  1147.   // If the AppBar is docked and the hittest code is a resize code...
  1148.   if ((GetEdge <> abeFloat) and (GetEdge <> abeUnknown) and
  1149.       (HTSIZEFIRST <= u) and (u <= HTSIZELAST)) then begin
  1150.  
  1151.     if (IsEdgeLeftOrRight(GetEdge) and (FszSizeInc.cx = 0)) or
  1152.        (not IsEdgeLeftOrRight(GetEdge) and (FszSizeInc.cy = 0)) then begin
  1153.       // If the width/height size increment is zero, then resizing is NOT
  1154.       // allowed for the edge that the AppBar is docked on
  1155.       u := HTBORDER; // Pretend that the mouse is not on a resize border
  1156.     end else begin
  1157.       // Resizing IS allowed for the edge that the AppBar is docked on
  1158.       // Get the location of the appbar's client area in screen coordinates
  1159.       rcClient := GetClientRect;
  1160.       pt.X := rcClient.Left;
  1161.       pt.Y := rcClient.Top;
  1162.       pt := ClientToScreen(pt);
  1163.       rcClient.Left := pt.X;
  1164.       rcClient.Top  := pt.Y;
  1165.       pt.X := rcClient.Right;
  1166.       pt.Y := rcClient.Bottom;
  1167.       pt := ClientToScreen(pt);
  1168.       rcClient.Right  := pt.X;
  1169.       rcClient.Bottom := pt.Y;
  1170.  
  1171.       u := HTBORDER;  // Assume that we can't resize
  1172.       case GetEdge of
  1173.         abeLeft:
  1174.           if Msg.XPos > rcClient.Right then
  1175.             u := HTRIGHT;
  1176.         abeTop:
  1177.           if Msg.YPos > rcClient.Bottom then
  1178.             u := HTBOTTOM;
  1179.         abeRight:
  1180.           if Msg.XPos < rcClient.Left then
  1181.             u := HTLEFT;
  1182.         abeBottom:
  1183.           if Msg.YPos < rcClient.Top then
  1184.             u := HTTOP;
  1185.       end; // end of case
  1186.     end; // end of else
  1187.   end;
  1188.  
  1189.   // Return the hittest code
  1190.   Msg.Result := u;
  1191. end;
  1192.  
  1193.  
  1194. // TAppBar.OnEnterSizeMove ////////////////////////////////////////////////////
  1195. procedure TAppBar.OnEnterSizeMove (var Msg : TMessage);
  1196. begin
  1197.   // The user started moving/resizing the AppBar, save its current state
  1198.   FabEdgeProposedPrev := GetEdge;
  1199. end;
  1200.  
  1201.  
  1202. // TAppBar.OnExitSizeMove /////////////////////////////////////////////////////
  1203. procedure TAppBar.OnExitSizeMove (var Msg : TMessage);
  1204. var
  1205.   abEdgeProposedPrev : TAppBarEdge;
  1206.   rc : TRect;
  1207. begin
  1208.   // The user stopped moving/resizing the AppBar, set the new state
  1209.   // Save the new proposed state of the AppBar
  1210.   abEdgeProposedPrev := FabEdgeProposedPrev;
  1211.  
  1212.   // Set the proposed state back to unknown.  This causes GetState
  1213.   // to return the current state rather than the proposed state
  1214.   FabEdgeProposedPrev := abeUnknown;
  1215.  
  1216.   // Get the location of the window in screen coordinates
  1217.   GetWindowRect(Handle, rc);
  1218.  
  1219.   // If the AppBar's state has changed...
  1220.   if GetEdge = abEdgeProposedPrev then
  1221.     case GetEdge of
  1222.       abeLeft:
  1223.         // Save the new width of the docked AppBar
  1224.         FABS.rcDockDims.Left := rc.Right - rc.Left;
  1225.       abeTop:
  1226.         // Save the new height of the docked AppBar
  1227.         FABS.rcDockDims.Top := rc.Bottom - rc.Top;
  1228.       abeRight:
  1229.         // Save the new width of the docked AppBar
  1230.         FABS.rcDockDims.Right := rc.Right - rc.Left;
  1231.       abeBottom:
  1232.         // Save the new height of the docked AppBar
  1233.         FABS.rcDockDims.Bottom := rc.Bottom - rc.Top;
  1234.     end;
  1235.  
  1236.   // Always save the new position of the floating AppBar
  1237.   if abEdgeProposedPrev = abeFloat then
  1238.     FABS.rcFloat := rc;
  1239.  
  1240.   // After setting the dimensions, set the AppBar to the proposed state
  1241.   SetEdge(abEdgeProposedPrev);
  1242. end;
  1243.  
  1244.  
  1245. // TAppBar.OnMoving ///////////////////////////////////////////////////////////
  1246. procedure TAppBar.OnMoving (var Msg : TMessage);
  1247. var
  1248.   prc            : PRect;
  1249.   pt             : TSmallPoint;
  1250.   abEdgeProposed : TAppBarEdge;
  1251.   w, h           : Integer;
  1252. begin
  1253.   // We control the moving of the AppBar.  For example, if the mouse moves
  1254.   // close to an edge, we want to dock the AppBar
  1255.   // The lParam contains the window's position proposed by the system
  1256.   prc := PRect(Msg.LParam);
  1257.  
  1258.   // Get the location of the mouse cursor
  1259.   pt := GetMessagePosition;
  1260.  
  1261.   // Where should the AppBar be based on the mouse position?
  1262.   abEdgeProposed := CalcProposedState(pt);
  1263.  
  1264.   if ((FabEdgeProposedPrev <> abeFloat) and
  1265.       (abEdgeProposed = abeFloat)) then begin
  1266.     // While moving, the user took us from a docked/autohidden state to
  1267.     // the float state.  We have to calculate a rectangle location so that
  1268.     // the mouse cursor stays inside the window.
  1269.     prc^ := GetFloatRect;
  1270.     w := prc^.Right - prc^.Left;
  1271.     h := prc^.Bottom - prc^.Top;
  1272.     with prc^ do begin
  1273.       Left   := pt.X - w div 2;
  1274.       Top    := pt.Y;
  1275.       Right  := pt.X - w div 2 + w;
  1276.       Bottom := pt.Y + h;
  1277.     end;
  1278.   end;
  1279.  
  1280.   // Remember the most-recently proposed state
  1281.   FabEdgeProposedPrev := abEdgeProposed;
  1282.  
  1283.   // Tell the system where to move the window based on the proposed state
  1284.   GetRect(abEdgeProposed, prc^);
  1285.  
  1286.   // Tell our derived class that there is a proposed state change
  1287.   OnAppBarStateChange(True, abEdgeProposed);
  1288. end;
  1289.  
  1290.  
  1291. // TAppBar.OnSizing ///////////////////////////////////////////////////////////
  1292. procedure TAppBar.OnSizing (var Msg : TMessage);
  1293. var
  1294.   prc : PRect;
  1295.   rcBorder : TRect;
  1296.   nWidthNew, nHeightNew : Integer;
  1297. begin
  1298.   // We control the sizing of the AppBar.  For example, if the user re-sizes
  1299.   // an edge, we want to change the size in descrete increments.
  1300.   // The lParam contains the window's position proposed by the system
  1301.   prc := PRect(Msg.LParam);
  1302.  
  1303.   // Get the minimum size of the window assuming it has no client area.
  1304.   // This is the width/height of the window that must always be present
  1305.   rcBorder := Rect(0, 0, 0, 0);
  1306.   AdjustWindowRectEx(rcBorder,
  1307.                      GetWindowLong(Handle, GWL_STYLE),
  1308.                      False,
  1309.                      GetWindowLong(Handle, GWL_EXSTYLE));
  1310.  
  1311.   // We force the window to resize in discrete units set by the FszSizeInc
  1312.   // member.  From the new, proposed window dimensions passed to us, round
  1313.   // the width/height to the nearest discrete unit
  1314.   if FszSizeInc.cx <> 0 then
  1315.     nWidthNew  := ((prc^.Right - prc^.Left) - (rcBorder.Right - rcBorder.Left)
  1316.                    + FszSizeInc.cx div 2) div FszSizeInc.cx * FszSizeInc.cx
  1317.                   + (rcBorder.Right - rcBorder.Left)
  1318.   else
  1319.     nWidthNew  := prc^.Right - prc^.Left;
  1320.  
  1321.   if FszSizeInc.cy <> 0 then
  1322.     nHeightNew := ((prc^.Bottom - prc^.Top) - (rcBorder.Bottom - rcBorder.Top)
  1323.                    + FszSizeInc.cy div 2) div FszSizeInc.cy * FszSizeInc.cy
  1324.                   + (rcBorder.Bottom - rcBorder.Top)
  1325.   else
  1326.     nHeightNew := prc^.Bottom - prc^.Top;
  1327.  
  1328.   // Adjust the rectangle's dimensions
  1329.   case Msg.wParam of
  1330.     WMSZ_LEFT:
  1331.       prc^.Left   := prc^.Right  - nWidthNew;
  1332.  
  1333.     WMSZ_TOP:
  1334.       prc^.Top    := prc^.Bottom - nHeightNew;
  1335.  
  1336.     WMSZ_RIGHT:
  1337.       prc^.Right  := prc^.Left   + nWidthNew;
  1338.  
  1339.     WMSZ_BOTTOM:
  1340.       prc^.Bottom := prc^.Top    + nHeightNew;
  1341.  
  1342.     WMSZ_BOTTOMLEFT: begin
  1343.       prc^.Bottom := prc^.Top    + nHeightNew;
  1344.       prc^.Left   := prc^.Right  - nWidthNew;
  1345.     end;
  1346.  
  1347.     WMSZ_BOTTOMRIGHT: begin
  1348.       prc^.Bottom := prc^.Top    + nHeightNew;
  1349.       prc^.Right  := prc^.Left   + nWidthNew;
  1350.     end;
  1351.  
  1352.     WMSZ_TOPLEFT: begin
  1353.       prc^.Left   := prc^.Right  - nWidthNew;
  1354.       prc^.Top    := prc^.Bottom - nHeightNew;
  1355.     end;
  1356.  
  1357.     WMSZ_TOPRIGHT: begin
  1358.       prc^.Top    := prc^.Bottom - nHeightNew;
  1359.       prc^.Right  := prc^.Left   + nWidthNew;
  1360.     end;
  1361.   end; // end of case
  1362. end;
  1363.  
  1364.  
  1365. // TAppBar.OnGetMinMaxInfo ////////////////////////////////////////////////////
  1366. procedure TAppBar.OnGetMinMaxInfo (var Msg : TWMGetMinMaxInfo);
  1367. begin
  1368.   if GetEdge = abeFloat then
  1369.     with Msg.MinMaxInfo^ do begin
  1370.       ptMinTrackSize.X := FABS.nMinWidth;
  1371.       ptMinTrackSize.Y := FABS.nMinHeight;
  1372.       ptMaxTrackSize.X := FABS.nMaxWidth;
  1373.       ptMaxTrackSize.Y := FABS.nMaxHeight;
  1374.     end
  1375.   else
  1376.     with Msg.MinMaxInfo^ do begin
  1377.       ptMinTrackSize.X := 0;
  1378.       ptMinTrackSize.Y := 0;
  1379.       ptMaxTrackSize.X := GetSystemMetrics(SM_CXSCREEN);
  1380.       ptMaxTrackSize.Y := GetSystemMetrics(SM_CYSCREEN);
  1381.       if not IsEdgeTopOrBottom(GetEdge) then
  1382.         ptMaxTrackSize.X := ptMaxTrackSize.X div 2;
  1383.       if not IsEdgeLeftOrRight(GetEdge) then
  1384.         ptMaxTrackSize.Y := ptMaxTrackSize.Y div 2;
  1385.     end;
  1386. end;
  1387.  
  1388.  
  1389. { AppBar-specific helper functions }
  1390.  
  1391.  
  1392. // TAppBar.IsEdgeLeftOrRight //////////////////////////////////////////////////
  1393. function TAppBar.IsEdgeLeftOrRight (abEdge : TAppBarEdge) : Boolean;
  1394. begin
  1395.   Result := (abEdge in [abeLeft, abeRight]);
  1396. end;
  1397.  
  1398.  
  1399. // TAppBar.IsEdgeTopOrBottom //////////////////////////////////////////////////
  1400. function TAppBar.IsEdgeTopOrBottom (abEdge : TAppBarEdge) : Boolean;
  1401. begin
  1402.   Result := (abEdge in [abeTop, abeBottom]);
  1403. end;
  1404.  
  1405.  
  1406. // TAppBar.IsFloating /////////////////////////////////////////////////////////
  1407. function TAppBar.IsFloating (abEdge : TAppBarEdge) : Boolean;
  1408. begin
  1409.   Result := (abEdge = abeFloat);
  1410. end;
  1411.  
  1412.  
  1413. // TAppBar.IsDockable /////////////////////////////////////////////////////////
  1414. function TAppBar.IsDockable (abFlags : TAppBarFlags) : Boolean;
  1415. begin
  1416.   Result := ((abFlags * [abfAllowLeft .. abfAllowBottom]) <> []);
  1417. end;
  1418.  
  1419.  
  1420. // TAppBar.IsDockableVertically ///////////////////////////////////////////////
  1421. function TAppBar.IsDockableVertically (abFlags : TAppBarFlags) : Boolean;
  1422. begin
  1423.   Result := ((abFlags * [abfAllowLeft, abfAllowRight]) <> []);
  1424. end;
  1425.  
  1426.  
  1427. // TAppBar.IsDockableHorizontally /////////////////////////////////////////////
  1428. function TAppBar.IsDockableHorizontally (abFlags : TAppBarFlags) : Boolean;
  1429. begin
  1430.   Result := ((abFlags * [abfAllowTop, abfAllowBottom]) <> []);
  1431. end;
  1432.  
  1433.  
  1434. // TAppBar.ResetSystemKnowledge ///////////////////////////////////////////////
  1435. procedure TAppBar.ResetSystemKnowledge;
  1436. {$ifdef DEBUG}
  1437. var
  1438.   abd : TAppBarData;
  1439. begin
  1440.   abd.cbSize := sizeof(abd);
  1441.   abd.hWnd := 0;
  1442.   SHAppBarMessage(ABM_REMOVE, abd);
  1443. end;
  1444. {$else}
  1445. begin
  1446.   // nothing to do when not in debug mode
  1447. end;
  1448. {$endif}
  1449.  
  1450.  
  1451. // TAppBar.GetEdgeFromPoint ///////////////////////////////////////////////////
  1452. function TAppBar.GetEdgeFromPoint (abFlags : TAppBarFlags;
  1453.                                    pt      : TSmallPoint) : TAppBarEdge;
  1454. var
  1455.   rc             : TRect;
  1456.   cxScreen       : Integer;
  1457.   cyScreen       : Integer;
  1458.   ptCenter       : TSmallPoint;
  1459.   ptOffset       : TSmallPoint;
  1460.   bIsLeftOrRight : Boolean;
  1461.   abSubstEdge    : TAppBarEdge;
  1462. begin
  1463.   // Let's get floating out of the way first
  1464.   if abfAllowFloat in abFlags then begin
  1465.  
  1466.     // Get the rectangle that bounds the size of the screen
  1467.     // minus any docked (but not-autohidden) AppBars
  1468.     SystemParametersInfo(SPI_GETWORKAREA, 0, @rc, 0);
  1469.  
  1470.     // Leave a 1/2 width/height-of-a-scrollbar gutter around the workarea
  1471.     InflateRect(rc,
  1472.                 -GetSystemMetrics(SM_CXVSCROLL),
  1473.                 -GetSystemMetrics(SM_CYHSCROLL));
  1474.  
  1475.     // If the point is in the adjusted workarea OR no edges are allowed
  1476.     if PtInRect(rc, SmallPointToPoint(pt)) or
  1477.        not IsDockable(abFlags) then begin
  1478.       // The AppBar should float
  1479.       Result := abeFloat;
  1480.       Exit;
  1481.     end;
  1482.   end;
  1483.  
  1484.   // If we get here, the AppBar should be docked; determine the proper edge
  1485.   // Get the dimensions of the screen
  1486.   cxScreen := GetSystemMetrics(SM_CXSCREEN);
  1487.   cyScreen := GetSystemMetrics(SM_CYSCREEN);
  1488.  
  1489.   // Find the center of the screen
  1490.   ptCenter.X := cxScreen div 2;
  1491.   ptCenter.Y := cyScreen div 2;
  1492.  
  1493.   // Find the distance from the point to the center
  1494.   ptOffset.X := pt.X - ptCenter.X;
  1495.   ptOffset.Y := pt.Y - ptCenter.Y;
  1496.  
  1497.   // Determine if the point is farther from the left/right or top/bottom
  1498.   bIsLeftOrRight :=
  1499.     ((Abs(ptOffset.Y) * cxScreen) <= (Abs(ptOffset.X) * cyScreen));
  1500.  
  1501.   // Propose an edge
  1502.   if bIsLeftOrRight then begin
  1503.     if 0 <= ptOffset.X then
  1504.       Result := abeRight
  1505.     else
  1506.       Result := abeLeft;
  1507.   end else begin
  1508.     if 0 <= ptOffset.Y then
  1509.       Result := abeBottom
  1510.     else
  1511.       Result := abeTop;
  1512.   end;
  1513.  
  1514.   // Calculate an edge substitute
  1515.   if abfAllowFloat in abFlags then
  1516.     abSubstEdge := abeFloat
  1517.   else
  1518.     abSubstEdge := FABS.abEdge;
  1519.  
  1520.   // Check if the proposed edge is allowed. If not, return the edge substitute
  1521.   case Result of
  1522.     abeLeft  : if not (abfAllowLeft   in abFlags) then Result := abSubstEdge;
  1523.     abeTop   : if not (abfAllowTop    in abFlags) then Result := abSubstEdge;
  1524.     abeRight : if not (abfAllowRight  in abFlags) then Result := abSubstEdge;
  1525.     abeBottom: if not (abfAllowBottom in abFlags) then Result := abSubstEdge;
  1526.   end;
  1527.  
  1528. end;
  1529.  
  1530.  
  1531. { Public member functions }
  1532.  
  1533.  
  1534. // TAppBar.Create /////////////////////////////////////////////////////////////
  1535. constructor TAppBar.Create (Owner : TComponent);
  1536. begin
  1537.   // Force the shell to update its list of AppBars and the workarea.
  1538.   // This is a precaution and is very useful when debugging.  If you create
  1539.   // an AppBar and then just terminate the application, the shell still
  1540.   // thinks that the AppBar exists and the user's workarea is smaller than
  1541.   // it should be.  When a new AppBar is created, calling this function
  1542.   // fixes the user's workarea.
  1543.   ResetSystemKnowledge;
  1544.  
  1545.   // Set default state of AppBar to float with no width & height
  1546.   FABS.cbSize            := sizeof(TAppBarSettings);
  1547.   FABS.abEdge            := abeFloat;
  1548.   FABS.bAutohide         := False;
  1549.   FABS.bAlwaysOnTop      := True;
  1550.   FABS.rcDockDims.Left   := AB_DEF_DOCK_DIM;
  1551.   FABS.rcDockDims.Top    := AB_DEF_DOCK_DIM;
  1552.   FABS.rcDockDims.Right  := AB_DEF_DOCK_DIM;
  1553.   FABS.rcDockDims.Bottom := AB_DEF_DOCK_DIM;
  1554.   FABS.rcFloat.Left      := 0;
  1555.   FABS.rcFloat.Top       := 0;
  1556.   FABS.rcFloat.Right     := 0;
  1557.   FABS.rcFloat.Bottom    := 0;
  1558.   FABS.nMinWidth         := 0;
  1559.   FABS.nMinHeight        := 0;
  1560.   FABS.nMaxWidth         := GetSystemMetrics(SM_CXSCREEN);
  1561.   FABS.nMaxHeight        := GetSystemMetrics(SM_CYSCREEN);
  1562.   FabFlags               := [abfAllowLeft .. abfAllowFloat];
  1563.   FszSizeInc.cx          := AB_DEF_SIZE_INC;
  1564.   FszSizeInc.cy          := AB_DEF_SIZE_INC;
  1565.   FabEdgeProposedPrev    := abeUnknown;
  1566.   FbFullScreenAppOpen    := False;
  1567.   FbAutoHideIsVisible    := False;
  1568.  
  1569.   // Call base class
  1570.   inherited Create(Owner);
  1571. end;
  1572.  
  1573.  
  1574. // TAppBar.Destroy ////////////////////////////////////////////////////////////
  1575. destructor TAppBar.Destroy;
  1576. begin
  1577.   ResetSystemKnowledge;
  1578.  
  1579.   // Call base class
  1580.   inherited Destroy;
  1581. end;
  1582.  
  1583.  
  1584. // TAppBar.UpdateBar //////////////////////////////////////////////////////////
  1585. procedure TAppBar.UpdateBar;
  1586. begin
  1587.   SetEdge(GetEdge);
  1588. end;
  1589.  
  1590. end.
  1591.